<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Peaks.js Demo Page</title>
    <link rel="stylesheet" type="text/css" href="style.css">
  </head>
  <body>
    <div id="titles">
      <h1>Peaks.js</h1>
      <p>
        Peaks.js is a JavaScript library that allows you to display and
        interact with audio waveforms in the browser.
      </p>

      <h2>Demo pages</h2>
      <p>
        The following pages demonstrate various configuration options:
      </p>
      <p>
        <a href="/index.html">Precomputed Waveform Data</a> |
        <a href="/webaudio.html">Web Audio API</a> |
        <a href="/zoomable-waveform.html">Single Zoomable Waveform</a> |
        <a href="/overview-waveform.html">Single Fixed Waveform</a> |
        <a href="/cue-events.html">Cue Events</a> |
        <a href="/set-source.html">Changing the Media URL</a> |
        <a href="/multi-channel.html">Multi-Channel Waveform</a> |
        <a href="/custom-markers">Custom Point and Segment Markers</a> |
        <a href="/overlay-segments.html">Overlay Segments</a> |
        External Audio Player |
        <a href="/scrollbar.html">Scrollbar</a>
      </p>
      <h2>Demo: External Player</h2>
      <p>
        This demo shows how to use an external player for audio playback with Peaks.js.
        A Tone.js player is set up in the example.
      </p>
    </div>

    <div class="waveform-container">
      <div id="zoomview-container"></div>
    </div>
    <div class="waveform-container">
      <div id="overview-container"></div>
    </div>

    <div id="demo-controls">
      <div id="controls">
        <div>
          <button data-action="play">Play</button>
          <button data-action="pause">Pause</button>
          <input type="text" id="seek-time" value="0.0">
          <button data-action="seek">Seek</button>
          <button data-action="zoom-in">Zoom in</button>
          <button data-action="zoom-out">Zoom out</button>
          <input type="text" id="segment-start" value="2.0">
          <input type="text" id="segment-end" value="5.0">
          <button data-action="play-segment">Play segment</button>
        </div>
        <div>
          <label for="select-audio">Select audio:</label>
          <select id="select-audio"></select>
        </div>
      </div>
    </div>

    <script src="/peaks.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.77/Tone.js" integrity="sha512-gwSDP1iEKl4KG0wi3N89RAJU91s78jb/TGC6lsbF5IyR2c19Rn7Jl1icK4K4kUiJNwQUEPI7o98T+GJVWNuvIQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    <script>
      function getSources() {
        return [
          {
            title: 'Tree of Life',
            mediaUrl: '/TOL_6min_720p_download.mp3',
          },
          {
            title: 'Desert Island Discs',
            mediaUrl: '/sample.mp3',
          },
          {
            title: 'BBC Sound Effect (Cars)',
            mediaUrl: '/07023003.mp3',
          }
        ];
      }

      function fetchAndDecode(audioContext, url) {
        // Load audio file into an AudioBuffer
        return fetch(url)
          .then(function(response) {
            return response.arrayBuffer();
          })
          .then(function(buffer) {
            return audioContext.decodeAudioData(buffer);
          });
      }

      function fetchAndUpdateWaveform(peaksInstance, audioContext, source) {
        return fetchAndDecode(audioContext, source.mediaUrl)
          .then(function(audioBuffer) {
            // Update the Peaks.js waveform view with the new AudioBuffer.
            // The AudioBuffer is passed through to the external player's setSource()
            // method, to update the player.
            peaksInstance.setSource({
              webAudio: {
                audioBuffer: audioBuffer,
                scale: 128,
                multiChannel: false
              }
            }, function(error) {
              if (error) {
                console.error('setSource error', error);
              }
            });
          });
      }

      (async function(Peaks) {
        const audioContext = Tone.context;

        fetchAndDecode(audioContext, "/TOL_6min_720p_download.mp3")
          .then(function(audioBuffer) {
            const player = {
              externalPlayer: new Tone.Player(audioBuffer).toDestination(),
              eventEmitter: null,

              init: function(eventEmitter) {
                this.eventEmitter = eventEmitter;

                this.externalPlayer.sync();
                this.externalPlayer.start();

                Tone.Transport.scheduleRepeat(() => {
                  const time = this.getCurrentTime();
                  eventEmitter.emit('player.timeupdate', time);

                  if (time >= this.getDuration()) {
                    Tone.Transport.stop();
                  }
                }, 0.25);

                return Promise.resolve();
              },

              destroy: function() {
                Tone.context.dispose();

                this.externalPlayer = null;
                this.eventEmitter = null;
              },

              setSource: function(opts) {
                if (this.isPlaying()) {
                  this.pause();
                }

                // Update the Tone.js Player object with the new AudioBuffer
                this.externalPlayer.buffer.set(opts.webAudio.audioBuffer);

                return Promise.resolve();
              },

              play: function() {
                return Tone.start().then(() => {
                  Tone.Transport.start();

                  this.eventEmitter.emit('player.playing', this.getCurrentTime());
                });
              },

              pause: function() {
                Tone.Transport.pause();

                this.eventEmitter.emit('player.pause', this.getCurrentTime());
              },

              isPlaying: function() {
                return Tone.Transport.state === "started";
              },

              seek: function(time) {
                Tone.Transport.seconds = time;

                this.eventEmitter.emit('player.seeked', this.getCurrentTime());
                this.eventEmitter.emit('player.timeupdate', this.getCurrentTime());
              },

              isSeeking: function() {
                return false;
              },

              getCurrentTime: function() {
                return Tone.Transport.seconds;
              },

              getDuration: function() {
                return this.externalPlayer.buffer.duration;
              }
            };

            const options = {
              zoomview: {
                container: document.getElementById('zoomview-container')
              },
              overview: {
                container: document.getElementById('overview-container')
              },
              player: player,
              webAudio: {
                audioBuffer: audioBuffer,
                scale: 128,
                multiChannel: false
              },
              keyboard: true,
              showPlayheadTime: true,
              zoomLevels: [128, 256, 512, 1024, 2048, 4096]
            };

            Peaks.init(options, function(err, peaksInstance) {
              if (err) {
                console.error(err.message);
                return;
              }

              console.log('Peaks instance ready');

              document.querySelector('[data-action="play"]').addEventListener('click', function() {
                peaksInstance.player.play();
              });

              document.querySelector('[data-action="pause"]').addEventListener('click', function() {
                peaksInstance.player.pause();
              });

              document.querySelector('button[data-action="seek"]').addEventListener('click', function(event) {
                const time = document.getElementById('seek-time').value;
                const seconds = parseFloat(time);

                if (!Number.isNaN(seconds)) {
                  peaksInstance.player.seek(seconds);
                }
              });

              document.querySelector('[data-action="zoom-in"]').addEventListener('click', function() {
                peaksInstance.zoom.zoomIn();
              });

              document.querySelector('[data-action="zoom-out"]').addEventListener('click', function() {
                peaksInstance.zoom.zoomOut();
              });

              document.querySelector('[data-action="play-segment"]').addEventListener('click', function () {
                const start = document.getElementById('segment-start').value;
                const startInSeconds = parseFloat(start);

                const end = document.getElementById('segment-end').value;
                const endInSeconds = parseFloat(end);

                peaksInstance.player.playSegment({ startTime: startInSeconds, endTime: endInSeconds, editable: true });
              });

              const select = document.getElementById('select-audio');

              const sources = getSources();

              for (let i = 0; i < sources.length; i++) {
                select.options[i] = new Option(sources[i].title, i);
              }

              select.addEventListener('change', function(event) {
                const source = sources[event.target.value];

                fetchAndUpdateWaveform(peaksInstance, audioContext, source);
              });
            });
          });
      })(peaks);
    </script>
  </body>
</html>
